#include "dht11.h"

static GPIO_InitTypeDef GPIO_InitStructure;

// 初始化温湿度模块（PG9）
void dht11_Init(void)
{
	/* 1.打开外设时钟(根据需求再开启，节省功耗) */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);

	/* 2.配置GPIO */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;		   // GPIO引脚编号
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;	   // 模式：输出模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;	   // 类型：推挽
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; // 输出速率：跟功耗相关
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;   // 无上下拉
	GPIO_Init(GPIOG, &GPIO_InitStructure);

	// 3.给温湿度模块一个初始化电平
	GPIO_SetBits(GPIOG, GPIO_Pin_9);
}

// 获取温湿度
int8_t dht11_read_data(uint8_t pbuf[5])
{
	uint8_t data;
	int8_t i, j;

	// 1.修改PG9为输出模式，初始电平为高电平
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; // 模式：输出模式
	GPIO_Init(GPIOG, &GPIO_InitStructure);
	DHT11_OUT = 1;
	delay_ms(1); // 添加一个小延时（确保电平变化能够被模块识别）

	// 2.发送开始信号
	DHT11_OUT = 0;
	delay_ms(20); // 至少18ms

	// 3.拉高，修改PG9为输入模式
	DHT11_OUT = 1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; // 模式：输出模式
	GPIO_Init(GPIOG, &GPIO_InitStructure);
	delay_us(30); // 20~40us

	// 4.检测DHT11是否正常（如被模块拉低）
	while (DHT11_IN)
		; // 未添加超时检测

	// 5.等待响应信号(低电平)结束
	while (!DHT11_IN)
		; // 未添加超时检测

	// 6.等待准备输出数据信号(高电平)结束
	while (DHT11_IN)
		; // 未添加超时检测

	// 7.开始接收模块发过来的数据（0或1）（难点）
	for (j = 0; j < 5; j++)
	{
		data = 0;				 // 0000 0000
		for (i = 7; i >= 0; i--) // MSB高位先出（拼接1个字节）
		{
			// 7.1 等待前50us低电平结束
			while (!DHT11_IN)
				; // 未添加超时检测

			// 7.2 延时一段时间（40~60us）
			delay_us(50);

			// 7.3 开始判断当前电平为高1还是低0
			if (DHT11_IN)
			{
				data |= 1 << i;

				// 等待70us剩余时间结束
				while (DHT11_IN)
					; // 未添加超时检测
			}
		}

		pbuf[j] = data; // 将拼接好字节存储到数组中。
	}

	// 8.比对校验和（如不需要可不比对）
	//????

	return 0;
}

/**
 * @brief 读取dht11温湿度传感器的数据
 * 
 * @return DHT11_DataTypeDef 若为负数则获取失败，并返回对应步数的负数编号，正数或0获取成功
 */
DHT11_DataTypeDef dht11_read(void)
{
	uint8_t data, pbuf[5];
	int8_t i, j;
	uint8_t check_num; // 校验和
	DHT11_DataTypeDef dht11_data = {-1.0, -1.0};

	// 1.修改PG9为输出模式，初始电平为高电平
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; // 模式：输出模式
	GPIO_Init(GPIOG, &GPIO_InitStructure);
	DHT11_OUT = 1;
	delay_ms(5); // 添加一个小延时（确保电平变化能够被模块识别）

	// 2.发送开始信号
	DHT11_OUT = 0;
	delay_ms(20); // 至少18ms

	// 3.拉高，修改PG9为输入模式
	DHT11_OUT = 1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; // 模式：输出模式
	GPIO_Init(GPIOG, &GPIO_InitStructure);
	delay_us(30); // 20~40us

	// 4.检测DHT11是否正常（如被模块拉低）
	WHILE_TIMEOUT_US(DHT11_IN, 4000)
	{
		dht11_data.temperature = -4.0;
		dht11_data.humidity = -4.0;
		return dht11_data;
	}
	WHILE_TIMEOUT_US_END;

	// 5.等待响应信号(低电平)结束
	WHILE_TIMEOUT_US(!DHT11_IN, 100)
	{
		dht11_data.temperature = -5.0;
		dht11_data.humidity = -5.0;
		return dht11_data;
	}
	WHILE_TIMEOUT_US_END;

	// 6.等待准备输出数据信号(高电平)结束
	WHILE_TIMEOUT_US(DHT11_IN, 100)
	{
		dht11_data.temperature = -6.0;
		dht11_data.humidity = -6.0;
		return dht11_data;
	}
	WHILE_TIMEOUT_US_END;

	// 7.开始接收模块发过来的数据（0或1）（难点）
	for (j = 0; j < 5; j++) // 接收5个字节的数据
	{
		data = 0;				 // 0000 0000 清零8个bit
		for (i = 7; i >= 0; i--) // MSB高位先出（拼接1个字节）将8个bit接收为1个字节
		{
			// 7.1 等待前50us低电平结束
			WHILE_TIMEOUT_US(!DHT11_IN, 100)
			{
				dht11_data.temperature = -7.0;
				dht11_data.humidity = -7.0;
				return dht11_data;
			}
			WHILE_TIMEOUT_US_END;

			delay_us(50); // 7.2 延时一段时间（40~60us）

			if (DHT11_IN) // 7.3 开始判断当前电平为高1还是低0
			{
				data |= 1 << i;					// 数据1，组合到对应bit位
				WHILE_TIMEOUT_US(DHT11_IN, 100) // 等待70us剩余时间结束
				{
					dht11_data.temperature = -7.0;
					dht11_data.humidity = -7.0;
					return dht11_data;
				}
				WHILE_TIMEOUT_US_END;
			}
		}

		pbuf[j] = data; // 将拼接好字节存储到数组中。
	}

	// 8.校验和
	check_num = (pbuf[0] + pbuf[1] + pbuf[2] + pbuf[3]) & 0xFF;
	if (check_num != pbuf[4])
	{
		dht11_data.temperature = -8.0;
		dht11_data.humidity = -8.0;
		return dht11_data;
	}

	// 9.封装结构体
	dht11_data.temperature = (float)pbuf[2] + ((float)pbuf[3] / (float)10);
	dht11_data.humidity = (float)pbuf[0]/* + ((float)pbuf[1] / (float)10)*/;

	return dht11_data;
}
